/**
 * Copyright (c) 2009-2011, chunquedong(YangJiandong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * History:
 *     2010-05-05  Jed Young  Creation
 */
package chunmap.model.coord;

import java.util.ArrayList;
import java.util.List;

import chunmap.model.elem.Envelope;
import chunmap.model.elem.EnvelopeBuf;
import chunmap.model.elem.LineSegment;

public class CoordSeqEditor {
	private List<Position> points;

    public CoordSeqEditor()
    {
        points = new ArrayList<Position>();
    }
    public CoordSeqEditor(Position[] pointList)
    {
        points = new ArrayList<Position>();
        for (Position p : pointList)
        {
            points.add(p);
        }
    }
    
    public CoordSeqEditor(List<Position> pointList)
    {
        points = new ArrayList<Position>();
        for (Position p : pointList)
        {
            points.add(p);
        }
    }
    public CoordSeqEditor(CoordinateSeq coordSeq)
    {
        points = new ArrayList<Position>();
        for (Position p : coordSeq)
        {
            points.add(p);
        }
    }

    @Override
    public String toString()
    {
        return "LineStringEditor [" + points + "]";
    }
    private Envelope getEnvelop()
    {
        EnvelopeBuf env = new EnvelopeBuf();
        for (Position p : points)
        {
            env.extendEnvelop(p);
        }
        return env.toEnvelop();
    }
    //------------------------------------------------------------------------

    //#region get

    public Position firstPoint()
    {
        return getPoint(0);
    }
    public Position lastPoint()
    {
        return getPoint(size() - 1);
    }
    public LineSegment getLineSegment(int i)
    {
        return new LineSegment(getPoint(i), getPoint(i + 1));
    }
    public int size()
    {
        return points.size();
    }
    public Position getPoint(int index)
    {
        return points.get(index);
    }
    public CoordinateSeq toCoordinateSeq()
    {
        return new CompactedCoordinateArray(points.toArray(new Position[0]));
    }

    //#endregion

    //------------------------------------------------------------------------

    //#region edit
    /**
    	 * if point is not on the line ,do nothing
    	 * 
    	 * @param p
    	 * @return
    	 */
    public void tryInsertPoint(Position p)
    {
        if (!this.getEnvelop().contain(p))
        {
            return;
        }

        for (int i = 0, n = size() - 1; i < n; i++)
        {
            LineSegment lseg = getLineSegment(i);
            if (lseg.onLineSegment(p))
            {
                if (lseg.onBorder(p))
                    return;
                else
                {
                    addAt(i + 1, p);
                    break;
                }
            }
        }
    }
    public void deletePointAt(int index)
    {
        points.remove(index);
    }
    /**
     * 强制闭合
     * 
     * @return
     */
    public void close()
    {
        if (!isClose())
        {
            Position lastPoint = points.get(size() - 1);
            if (points.get(0).approximateEquals(lastPoint))
            {
                points.remove(size() - 1);
            }
            addPointToLast(points.get(0));
        }
    }
    public void deleteOverPoint()
    {
        List<Position> ps = new ArrayList<Position>();
        for (Position p : points)
        {
            if (!containPoint(ps, p))
            {
                ps.add(p);
            }
        }
        points = ps;
    }
    private boolean containPoint(List<Position> ps, Position point)
    {
        for (Position p : ps)
        {
            if (p.approximateEquals(point))
                return true;
        }
        return false;
    }
    public boolean isClose()
    {
        Position lastPoint = points.get(size() - 1);
        return points.get(0).equals(lastPoint);
    }
    public void addPointToLast(Position p)
    {
        points.add(p);
    }
    public void addAt(int index, Position p)
    {
        points.add(index, p);
    }
    public boolean isEmpty()
    {
        if (size() == 0)
            return true;
        return false;
    }
    public void addAll(CoordinateSeq lineString)
    {
    	for(Position p: lineString){
    		points.add(p);
    		
    	}
    }
    public void insertAll(CoordinateSeq lineString)
    {
        List<Position> tpoints = new ArrayList<Position>();
        for (Position p : lineString)
        {
            tpoints.add(p);
        }
        points.addAll(0, tpoints);
    }
    public void tryInsertAll(CoordinateSeq lineString){
    	for (Position p : lineString)
        {
            this.tryInsertPoint(p);
        }
    }
    /**
     * 反向
     * 
     * @return
     */
    public void reverse()
    {
        List<Position> pointList = new ArrayList<Position>();
        for (int i = size() - 1; i >= 0; i--)
        {
            pointList.add(getPoint(i));
        }
        points = pointList;
    }
    //#endregion

    //------------------------------------------------------------------------

    /**
     * 是否能和另一条线首尾相接，然后调用join方法就不会出错了。
     * 
     * @param l2
     * @return
     */
    public boolean canJoin(CoordinateSeq l2)
    {
        if (isEmpty()) return true;

        Position endPoint = l2.endPoint();
        if ((lastPoint().equals(l2.startPoint()))
                || (this.lastPoint().equals(endPoint))
                || (this.firstPoint().equals(l2.startPoint()))
                || (this.firstPoint().equals(endPoint)))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    /**
     * 和另一条线连接在一起
     * 
     * @param l2
     * @return
     */
    public void join(CoordinateSeq l2)
    {
        if (this.isEmpty())
        {
            addAll(l2);
        }
        else if (lastPoint().equals(l2.startPoint()))
        {
            directAddJoin(l2);
        }
        else if (this.lastPoint().equals(l2.endPoint()))
        {
            directAddJoin(reverse(l2));
        }
        else if (this.firstPoint().equals(l2.startPoint()))
        {
            this.reverse();
            directAddJoin(l2);
        }
        else if (this.firstPoint().equals(l2.endPoint()))
        {
            directInsertJoin(l2);
        }
        else
        {
            throw new IllegalArgumentException("no join part");
        }
    }

    private CoordinateSeq reverse(CoordinateSeq ls)
    {
        CoordSeqEditor le = new CoordSeqEditor(ls);
        le.reverse();
        return le.toCoordinateSeq();
    }
    private void directAddJoin(CoordinateSeq line)
    {
        for (int i = 1, n = line.size(); i < n; i++)
        {
            Position point = line.getCoordinate(i);
            points.add(point);
        }
    }
    private void directInsertJoin(CoordinateSeq line)
    {
        List<Position> pointList = new ArrayList<Position>();
        for (int i = 0, n = line.size() - 1; i < n; i++)
        {
            Position point = line.getCoordinate(i);
            pointList.add(point);
        }
        points.addAll(0, pointList);
    }
}